/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.sd.chat;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Vector;
import java.util.logging.Level;

/**
 *
 * @author Old
 */
public class ConnectionHandler extends Thread implements sendControler, receiveControler, ClientHandlerInterface {

    ObjectInputStream in;
    ObjectOutputStream out;
    Socket clientSocket;
    int thread_number;
    private final Vector<TCPMsg> sendQueue;
    private final Vector<TCPMsg> receiveQueue;
    private ClientManager myManager;
    private boolean logged = false;
    private Utilizador loggedUser;
    private InputStream is;
    private OutputStream os;
    private boolean readyToSend = false;
    private boolean messageReceived = false;
    private boolean shutdown = false;
    private Logger log = new Logger(true);
    private OSReceiver rcvr;
    private OSSender sndr;

    public ConnectionHandler(Socket clientSocket, int thread_number, ClientManager myMan) {
        this.myManager = myMan;
        this.sendQueue = new Vector<TCPMsg>();
        this.receiveQueue = new Vector<TCPMsg>();
        this.clientSocket = clientSocket;
        this.thread_number = thread_number;
        try {
            is = this.clientSocket.getInputStream();
            os = this.clientSocket.getOutputStream();
            rcvr = new OSReceiver(this, is);           
            sndr = new OSSender(this);
        } catch (IOException e) {
            log.erro("Connection:" + e.getMessage());
        }
    }

    @Override
    public void run() {
        rcvr.start();
        sndr.start();
        while (!shutdown) {
            dispatchMessages();
        }
        shutdownAll();
        log.outln("ConnectionHandler terminating...");
    }
    
    public synchronized void shutdownAll(){
        sndr.setMustRun(false);
        rcvr.setMustRun(false);
        try {
            clientSocket.close();
        } catch (IOException ex) {
            clientSocket = null;
        }
        //rcvr.stop();
        notifyAll();        
    }

    public synchronized void dispatchMessages() {
        //while (!messageReceived) {
            if (this.receiveQueue.isEmpty()) {
                try {
                    log.outln("ConHandler dispatchMsss preparing to wait");
                    wait();
                } catch (InterruptedException e) {
                    log.erro("interruptedException caught");
                }
                log.outln("ConHandler dispatchMsss resuming");
            }

            while (!this.receiveQueue.isEmpty()) {
                TCPMsg rcvMsg = this.receiveQueue.remove(0);
                myManager.newMessageReceived(rcvMsg);
            }
            messageReceived = false;
        //}
    }

    @Override
    public synchronized void sendMessages() {
        //while (!readyToSend) {

            if (this.sendQueue.isEmpty()) {
                try {
                    log.outln("ConnectionHandler sendMessages preparing to wait.");
                    wait();
                } catch (InterruptedException e) {
                    log.erro("interruptedException caught");
                }
                log.outln("ConnectionHandler sendMessages resuming.");
            }
            while (!this.sendQueue.isEmpty()) {
                TCPMsg sendMsg = this.sendQueue.remove(0);
                log.outln("ConnectionHandler sendMessages sendingmessage: " + sendMsg);
                try {
                    out = new ObjectOutputStream(os);
                    this.out.writeObject(sendMsg);
                } catch (IOException ex) {
                    this.unexpectedShutdown();
                    log.erro("IO:" + ex);
                }
            }
            readyToSend = false;
        //}
    }

    @Override
    public synchronized void addMsg(TCPMsg newMsg) {
        this.sendQueue.add(newMsg);
        log.outln("ConnectionHandler nova mensagem para enviar: " + newMsg);
        readyToSend = true;
        notifyAll();
    }
    
    @Override
    public synchronized boolean canAddMsg(){
        return true;
    }

    public Socket getClientSocket() {
        return clientSocket;
    }
    
    @Override
    public synchronized void setSendQueue(Vector<TCPMsg> backupSendQueue){
        this.sendQueue.addAll(backupSendQueue);
        readyToSend = true;
        notifyAll();
    }

    public void setClientSocket(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    public ObjectInputStream getIn() {
        return in;
    }

    public void setIn(ObjectInputStream in) {
        this.in = in;
    }

    @Override
    public Utilizador getLoggedUser() {
        return loggedUser;
    }

    @Override
    public void setLoggedUser(Utilizador loggedUser) {
        this.loggedUser = loggedUser;
    }

    public ClientManager getMyManager() {
        return myManager;
    }

    public void setMyManager(ClientManager myManager) {
        this.myManager = myManager;
    }
    
    
    @Override
    public synchronized Vector<TCPMsg> getSendQueue(){
        Vector<TCPMsg> tempVec = new Vector<TCPMsg>();
        tempVec.addAll(this.sendQueue);
        return tempVec;
    }
    

    public ObjectOutputStream getOut() {
        return out;
    }

    public void setOut(ObjectOutputStream out) {
        this.out = out;
    }

    public int getThread_number() {
        return thread_number;
    }

    public void setThread_number(int thread_number) {
        this.thread_number = thread_number;
    }

    @Override
    public boolean isLogged() {
        return logged;
    }

    @Override
    public void setLogged(boolean logged) {
        this.logged = logged;
    }

    public synchronized boolean isShutdown() {
        return shutdown;
    }

    @Override
    public synchronized void setShutdown(boolean shutdown) {
        this.shutdown = shutdown;
        notifyAll();
    }
    
    

    @Override
    public synchronized boolean getMsgReceived() {
        return this.messageReceived;
    }

    @Override
    public synchronized void setMsgReceived(boolean state) {
        this.messageReceived = state;
    }

    @Override
    public synchronized void addReceivedMessage(TCPMsg message) {
        receiveQueue.add(message);
        this.messageReceived = true;
        notifyAll();
    }

    @Override
    public synchronized void unexpectedShutdown() {
        myManager.connectionLost();
        this.setShutdown(true);
        notifyAll();
    }
}
